package com.tianji.learning.utils;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.json.JSONUtil;
import com.tianji.common.utils.BeanUtils;
import com.tianji.common.utils.StringUtils;
import com.tianji.learning.domain.dto.LearningRecordFormDTO;
import com.tianji.learning.domain.po.LearningLesson;
import com.tianji.learning.domain.po.LearningRecord;
import com.tianji.learning.mapper.LearningLessonMapper;
import com.tianji.learning.mapper.LearningRecordMapper;
import lombok.Data;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import java.time.Duration;
import java.time.LocalDateTime;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.DelayQueue;

@Slf4j
@Component
@RequiredArgsConstructor
public class LearningRecordHandler {
    private final RedisTemplate redisTemplate;
    private final LearningLessonMapper lessonMapper;
    private final LearningRecordMapper recordMapper;
    private final static String KEY = "lessonId:{}:";
    private final static String HASH_KEY = "sectionId:{}:";
    DelayQueue<DelayQueueTask<RecordData>> delayeds = new DelayQueue<>();
    private static Boolean flag = true;

    //Bean创建时则开启一个线程监听延时队列
    @PostConstruct
    public void start() {
        log.info("Bean初始化阶段，开启一个新的线程执行延时检测任务...");
        CompletableFuture.runAsync(() -> checkLearningRecord());
    }

    @PreDestroy
    public void destory(){
        log.info("Bean销毁...");
        flag = false;
    }

    /**
     * 学习记录更新延时任务检测
     * @throws InterruptedException
     */
    public void checkLearningRecord(){
        try {
            while (flag) {
                DelayQueueTask<RecordData> task = delayeds.take();
                //1、获取延迟任务传递过来的recordData
                RecordData recordData = task.getData();
                log.info("从延时队列中获取到了20秒前提交的recordData:{}", recordData);
                //2、从缓存中查询对应的learningRecord
                LearningRecord learningRecord = getLeanringRecordFromCache(BeanUtils.toBean(recordData, LearningRecordFormDTO.class));
                //3、比较二者的moment
                if (ObjectUtil.notEqual(learningRecord.getMoment(), recordData.getMoment()) || ObjectUtil.isEmpty(learningRecord)) {
                    //4、如果moment不相等，则表示用户仍在继续观看，缓存持续更新，跳过该次任务
                    continue;
                }
                //5、如果moment相等，则表示用户已经离开
                // 5.1、将learningRecord更新到数据库中
                recordMapper.updateById(learningRecord);
                log.info("用户停止播放或离开，更新learningRecord到数据库中：{}", learningRecord);
                // 5.1、同时更新learningLessons表
                LearningLesson learningLesson = new LearningLesson();
                learningLesson.setLatestLearnTime(LocalDateTime.now().minusSeconds(20));
                learningLesson.setLatestSectionId(learningRecord.getSectionId());
                lessonMapper.updateById(learningLesson);
                log.info("同时更新learningLesson到数据库中：{}", learningLesson);
            }
        } catch (InterruptedException e) {
            log.error("执行播放进度延时检测任务失败！！！原因：{}", e);
        }
    }

    /**
     * 新增学习记录到redis中
     *
     * @param learningRecord
     */
    public void addLearningRecordToCache(LearningRecord learningRecord) {
        log.info("新增学习记录到redis中：{}", learningRecord);
        //1、构造Key
        String key = StringUtils.format(KEY, learningRecord.getLessonId());

        //2、构造HashKey
        String hashKey = StringUtils.format(HASH_KEY, learningRecord.getSectionId());

        //3、将learningRecord转为Json存入
        RedisData redisData = new RedisData(learningRecord);
        redisTemplate.opsForHash().put(key, hashKey, JSONUtil.toJsonStr(redisData));
        //添加一个一分钟的过期时间
        redisTemplate.expire(key,Duration.ofMinutes(1));
    }


    /**
     * 从redis中获取learningrecord
     *
     * @param learningRecordFormDTO
     * @return
     */
    public LearningRecord getLeanringRecordFromCache(LearningRecordFormDTO learningRecordFormDTO) {
        log.info("从redis中获取lessonId:{},sectionId:{}", learningRecordFormDTO.getLessonId(), learningRecordFormDTO.getSectionId());
        //1、构造Key
        String key = StringUtils.format(KEY, learningRecordFormDTO.getLessonId());

        //2、构造HashKey
        String hashKey = StringUtils.format(HASH_KEY, learningRecordFormDTO.getSectionId());

        //3、将Json转为learningRecord
        String learningRecordStr = (String) redisTemplate.opsForHash().get(key, hashKey);
        log.info("从redis中获取到的learningRecord:{}", learningRecordStr);
        if (ObjectUtil.isEmpty(learningRecordStr)){
            return null;
        }
        LearningRecord learningRecord = JSONUtil.toBean(learningRecordStr, LearningRecord.class);
        return learningRecord;
    }

    /**
     * 从redis中删除对应的learingRecord
     *
     * @param oldLearningRecord
     */
    public void deleteLeraningRecordFromCache(LearningRecord oldLearningRecord) {
        log.info("从redis中删除对应的learingRecord:{}", oldLearningRecord);
        //1、构造Key
        String key = StringUtils.format(KEY, oldLearningRecord.getLessonId());

        //2、构造HashKey
        String hashKey = StringUtils.format(HASH_KEY, oldLearningRecord.getSectionId());

        //3、将learningRecord转为Json
        redisTemplate.opsForHash().delete(key, hashKey);
    }

    /**
     * 将learningRecoed存入缓存并提交延迟检测任务
     *
     * @param newRecord
     */
    public void LearningRecordDelayCheckTask(LearningRecord newRecord) {
        log.info("将learningRecoed存入缓存并提交延迟检测任务：{}", newRecord);
        //1、先将learningRecord存入缓存
        addLearningRecordToCache(newRecord);
        //2、提交延迟检测任务
        delayeds.add(new DelayQueueTask<>(Duration.ofSeconds(20), new RecordData(newRecord)));

    }



    @Data
    class RedisData {
        private Long id;
        private Integer moment;
        private Boolean finished;

        public RedisData(LearningRecord learningRecord) {
            this.id = learningRecord.getId();
            this.moment = learningRecord.getMoment();
            this.finished = learningRecord.getFinished();
        }
    }

    @Data
    class RecordData {
        private Long lessonId;
        private Long sectionId;
        private Integer moment;

        public RecordData(LearningRecord newRecord) {
            this.lessonId = newRecord.getLessonId();
            this.sectionId = newRecord.getSectionId();
            this.moment = newRecord.getMoment();
        }
    }
}
